use crate::ffi::{
    Font12, Font20, Font24, Font8, Paint, Paint_Clear, Paint_ClearWindows, Paint_DrawBitMap,
    Paint_DrawChar, Paint_DrawCircle, Paint_DrawImage, Paint_DrawLine, Paint_DrawNum,
    Paint_DrawPoint, Paint_DrawRectangle, Paint_DrawString_EN, Paint_NewImage, Paint_SetMirroring,
    Paint_SetPixel, Paint_SetRotate, Paint_SetScale, DOT_PIXEL, DOT_STYLE, DRAW_FILL, LINE_STYLE,
};
use core::ptr::addr_of_mut;
use embed_std::infoln;

#[allow(unused, non_snake_case, non_camel_case_types, non_upper_case_globals)]
#[allow(improper_ctypes)] //for 128-bit integers don't currently have a known stable ABI
mod ffi;

pub use ffi::{BLACK, WHITE};
#[derive(Debug)]
pub enum Font {
    BM8,
    BM12,
    BM20,
    BM24,
}
#[repr(u8)]
#[derive(Debug, Clone)]
pub enum DotPixel {
    DotPixel1x1 = 1, // 1 x 1
    DotPixel2x2,     // 2 X 2
    DotPixel3x3,     // 3 X 3
    DotPixel4x4,     // 4 X 4
    DotPixel5x5,     // 5 X 5
    DotPixel6x6,     // 6 X 6
    DotPixel7x7,     // 7 X 7
    DotPixel8x8,     // 8 X 8
}

#[repr(u8)]
#[derive(Debug, Clone)]
pub enum DotStyle {
    DotFillAround = 1, // dot pixel 1 x 1
    DotFillRightup,    // dot pixel 2 X 2
}

#[repr(u8)]
#[derive(Debug, Clone)]
pub enum DrawStyle {
    DrawFillEmpty = 0,
    DrawFillFull,
}

#[repr(u8)]
#[derive(Debug, Clone)]
pub enum LineStyle {
    LineStyleSolid = 0,
    LineStyleDotted,
}

#[repr(u8)]
#[derive(Debug, Clone)]
pub enum DrawFill {
    DrawFillEmpty = 0,
    DrawFillFull,
}
#[repr(u16)]
#[derive(Debug, Clone)]
pub enum Rotate {
    R0 = 0,
    R90 = 90,
    R180 = 180,
    R270 = 270,
}

#[repr(u8)]
#[derive(Debug, Clone)]
pub enum Mirror {
    None = 0,
    Hor = 1,
    Ver = 2,
    Origin = 3,
}

#[derive(Debug, Clone)]
pub struct CanvasOpt {
    pub width: u16,
    pub height: u16,
    pub rotate: Rotate,
}
impl Default for CanvasOpt {
    fn default() -> Self {
        Self {
            width: 104,
            height: 212,
            rotate: Rotate::R0,
        }
    }
}

pub struct Canvas {
    image: Vec<u8>,
}

impl Canvas {
    pub fn new(opt: CanvasOpt) -> Canvas {
        let width = if opt.width % 8 == 0 {
            opt.width / 8
        } else {
            opt.width / 8 + 1
        };
        let size = width * opt.height;
        let mut image = Vec::with_capacity(size as usize);
        unsafe {
            image.set_len(size as usize);
            // image.fill(0xff);
            Paint_NewImage(
                image.as_mut_ptr(),
                opt.width,
                opt.height,
                opt.rotate as u16,
                WHITE as u16,
            );
        }
        Self { image }
    }
    pub fn data(&self) -> &[u8] {
        self.image.as_slice()
    }
    pub fn active(&mut self) {
        unsafe { Paint.Image = self.image.as_mut_ptr() }
    }
    pub fn set_rotate(&mut self, rotate: Rotate) {
        unsafe { Paint_SetRotate(rotate as u16) }
    }

    pub fn set_mirror(&mut self, mirror: Mirror) {
        unsafe { Paint_SetMirroring(mirror as u8) }
    }
    pub fn set_pixel(&mut self, x: u16, y: u16, color: u16) {
        unsafe { Paint_SetPixel(x, y, color) }
    }
    pub fn set_scale(&mut self, scale: u8) {
        unsafe { Paint_SetScale(scale) }
    }
    pub fn clear(&mut self, color: u16) {
        self.image.fill(0xff);
        unsafe { Paint_Clear(color) }
    }
    pub fn clear_rect(&mut self, x1: u16, y1: u16, x2: u16, y2: u16, color: u16) {
        unsafe { Paint_ClearWindows(x1, y1, x2, y2, color) }
    }
    pub fn draw_point(
        &mut self,
        x1: u16,
        y1: u16,
        color: u16,
        dot_pixcel: DotPixel,
        dot_style: DotStyle,
    ) {
        unsafe {
            Paint_DrawPoint(
                x1,
                y1,
                color,
                dot_pixcel as DOT_PIXEL,
                dot_style as DOT_STYLE,
            )
        }
    }
    pub fn draw_line(
        &mut self,
        x1: u16,
        y1: u16,
        x2: u16,
        y2: u16,
        color: u16,
        line_width: DotPixel,
        line_style: LineStyle,
    ) {
        unsafe {
            Paint_DrawLine(
                x1,
                y1,
                x2,
                y2,
                color,
                line_width as DOT_PIXEL,
                line_style as LINE_STYLE,
            )
        }
    }
    pub fn draw_rect(
        &mut self,
        x1: u16,
        y1: u16,
        x2: u16,
        y2: u16,
        color: u16,
        line_width: DotPixel,
        fill_style: DrawFill,
    ) {
        unsafe {
            Paint_DrawRectangle(
                x1,
                y1,
                x2,
                y2,
                color,
                line_width as DOT_PIXEL,
                fill_style as DRAW_FILL,
            )
        }
    }
    pub fn draw_circle(
        &mut self,
        x_center: u16,
        y_center: u16,
        radius: u16,
        color: u16,
        line_width: DotPixel,
        fill_style: DrawStyle,
    ) {
        unsafe {
            Paint_DrawCircle(
                x_center,
                y_center,
                radius,
                color,
                line_width as DOT_PIXEL,
                fill_style as DRAW_FILL,
            )
        }
    }
    pub fn draw_char(&mut self, x: u16, y: u16, s: u8, font: Font, fg_color: u16, bg_color: u16) {
        unsafe {
            let f = match font {
                Font::BM8 => addr_of_mut!(Font8),
                Font::BM12 => addr_of_mut!(Font12),
                Font::BM20 => addr_of_mut!(Font20),
                Font::BM24 => addr_of_mut!(Font24),
            };
            Paint_DrawChar(x, y, s as core::ffi::c_char, f, fg_color, bg_color)
        };
    }
    pub fn draw_string(
        &mut self,
        x: u16,
        y: u16,
        s: &str,
        font: Font,
        fg_color: u16,
        bg_color: u16,
    ) {
        let mut s = s.to_owned();
        s.push_str("\0");
        self.draw_cstring(x,y,s.as_bytes(), font, fg_color, bg_color)
    }
    pub fn draw_cstring(
        &mut self,
        x: u16,
        y: u16,
        s: &[u8],
        font: Font,
        fg_color: u16,
        bg_color: u16,
    ) {
        unsafe {
            let f = match font {
                Font::BM8 => addr_of_mut!(Font8),
                Font::BM12 => addr_of_mut!(Font12),
                Font::BM20 => addr_of_mut!(Font20),
                Font::BM24 => addr_of_mut!(Font24),
            };
            Paint_DrawString_EN(
                x,
                y,
                s.as_ptr() as *const core::ffi::c_char,
                f,
                bg_color,
                fg_color,
            )
        };
    }
    pub fn draw_number(
        &mut self,
        x: u16,
        y: u16,
        num: i32,
        font: Font,
        fg_color: u16,
        bg_color: u16,
    ) {
        unsafe {
            let f = match font {
                Font::BM8 => addr_of_mut!(Font8),
                Font::BM12 => addr_of_mut!(Font12),
                Font::BM20 => addr_of_mut!(Font20),
                Font::BM24 => addr_of_mut!(Font24),
            };
            Paint_DrawNum(x, y, num, f, fg_color, bg_color)
        };
    }
    pub fn draw_bitmap(&mut self, data: &[u8]) {
        unsafe { Paint_DrawBitMap(data.as_ptr()) }
    }
    pub fn draw_image(&mut self, x: u16, y: u16, w: u16, h: u16, data: &[u8]) {
        unsafe { Paint_DrawImage(data.as_ptr(), x, y, w, h) }
    }
}

impl Drop for Canvas {
    fn drop(&mut self) {
        infoln!("drop Canvas");
    }
}
